#include <netinet/in.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <iostream>

constexpr int DATA_BUFFER_SIZE = 4096;

static const int max_connection_backlog = 5;

int main(int argc, char **argv) {
    //if (argc != 2) {
    //    std::cout << "Usage: server [quickack:1]\n";
    //}
    int socket = ::socket(PF_INET, SOCK_STREAM, 0);

    struct sockaddr_in addr;
    bzero((char*)&addr, sizeof(addr));
    addr.sin_family      = AF_INET;
    addr.sin_port        = htons(8080);
    addr.sin_addr.s_addr = INADDR_ANY;

    if (argc == 2) {
        int flag = *argv[1] - '0';
        auto ret = ::setsockopt(socket, IPPROTO_TCP, TCP_QUICKACK, (char*)&flag, sizeof(flag));
        auto res = ret == 0 ? "True" : "False";
        std::cout << "setsockopt: quickack=" << flag << ", set success: " << res << "\n";
    }

    const int size_of_msg = 1 /* 100 */;

    bind(socket, (struct sockaddr*)&addr, sizeof(addr));

    listen(socket, max_connection_backlog);

    int finished = 0;
    while (!finished) {
        struct sockaddr_storage storage;
        socklen_t addr_size = sizeof(storage);
        int s = ::accept(socket, (struct sockaddr*)&storage, &addr_size);
        std::cout << "new connection in:\n";
        std::string msg;
        char buffer[DATA_BUFFER_SIZE];
        int cnt = 0;
        for (;;) {
            auto ret = read(s, buffer, DATA_BUFFER_SIZE-1);
            if (ret == 0) {
                break;
            }
            buffer[ret] = '\0';
            std::cout << "read: " << std::string(buffer, ret) << ", len=" << ret << "\n";
            msg += std::string(buffer, ret);
            cnt += ret;
            if (cnt == 2 * size_of_msg) {
                ret = write(s, msg.data(), msg.length());
                std::cout << "write: " << msg << "\n";
                msg.clear();
                cnt = 0;
            }
        }
        close(s);
        std::cout << "disconnected.\n";
    }
    close(socket);

    return 0;
}
